﻿//-------------------------------------------------------------------
//	Copyright (c) 2010-2014  Zhirnov Andrey
//	This file is part of the "UX Standard Lib" project.
//	See copyright notice in "UX_STL_Core.h".
//-------------------------------------------------------------------

#ifndef _UXS_MATH_UTILS_H
#define _UXS_MATH_UTILS_H

#pragma once

#include "Mathematics.h"
#include "TVec.h"
#include "TVec.h"
#include "TRect.h"

namespace UX_STL
{
namespace UXMath
{
	

/*
=================================================
	записывает все внутрение точки прямоугольника
=================================================
*/
	template <typename T, typename B>
	inline void _AddRectPoints(const TRect<T> &r, TVec<B,2> *pResult, uint &uCount)
	{
		for (T x = r.x; x < r.w; ++x)
		for (T y = r.y; y < r.h; ++y) {
			pResult[ uCount++ ] = TVec<B,2>( x, y );
		}
	}

	
	template <typename T, typename B>
	inline void BoolOpGetRectPoints(const TRect<T> &sRect, TVec<B,2> *pResult, uint &uCount)
	{
		uCount = 0;
		_AddRectPoints( sRect, pResult, uCount );
	}
	
/*
=================================================
	булевская операция, результат - массив
	прямоугольников оставшихся после вычитания
	r1 из r0
=================================================
*/
	template <typename T, typename B>
	inline void BoolOpRectMinusRect(const TRect<T> &r0, const TRect<T> &r1, TRect<B> *pResult, uint &uCount)
	{
		typedef TRect<T>	rect_t;

		uCount = 0;

		if ( not r0.IntersectRect( r1 ) )
		{
			pResult[ uCount++ ] = r0;
			return;
		}

		if ( r1.y > r0.y )	pResult[ uCount++ ] = rect_t( r0.x, r0.y, r0.w, r1.y );
		if ( r0.h > r1.h )	pResult[ uCount++ ] = rect_t( r0.x, r1.h, r0.w, r0.h );
		
		if ( r1.x > r0.x )	pResult[ uCount++ ] = rect_t( r0.x, Max( r0.y, r1.y ), r1.x, Min( r0.h, r1.h ) );
		if ( r0.w > r1.w )	pResult[ uCount++ ] = rect_t( r1.w, Max( r0.y, r1.y ), r0.w, Min( r0.h, r1.h ) );
	}
	
/*
=================================================
	булевская операция, результат - массив
	точек оставшихся после вычитания r1 из r0
=================================================
*/
	template <typename T, typename B>
	inline void BoolOpRectMinusRect(const TRect<T> &r0, const TRect<T> &r1, TVec<B,2> *pResult, uint &uCount)
	{
		typedef TRect<T>	rect_t;

		uCount = 0;

		if ( not r0.IntersectRect( r1 ) )
		{
			_AddRectPoints( r0, pResult, uCount );
			return;
		}
		
		if ( r1.y > r0.y ) {
			_AddRectPoints( rect_t( r0.x, r0.y, r0.w, r1.y ), pResult, uCount );
		}

		if ( r0.h > r1.h ) {
			_AddRectPoints( rect_t( r0.x, r1.h, r0.w, r0.h ), pResult, uCount );
		}
		
		if ( r1.x > r0.x ) {
			_AddRectPoints( rect_t( r0.x, Max( r0.y, r1.y ), r1.x, Min( r0.h, r1.h ) ), pResult, uCount );
		}

		if ( r0.w > r1.w ) {
			_AddRectPoints( rect_t( r1.w, Max( r0.y, r1.y ), r0.w, Min( r0.h, r1.h ) ), pResult, uCount );
		}
	}
	
/*
=================================================
	булевская операция, результат - массив
	прямоугольников образовавшихся при смещении
	(смещение относительно левого нижнего угла)
=================================================
*/
	template <typename T, typename B>
	inline void BoolOpRectOffset(const TRect<T> &sRect, const TVec<T,2> &vOffset,
								 TRect<B> *pResult, uint &uCount)
	{
		typedef TRect<T>	rect_t;

		const rect_t	r = sRect + vOffset;

		uCount = 0;

		if ( Any( Abs( vOffset ).LEqual( sRect.GetSize() ) ) )
		{
			pResult[ uCount++ ] = r;
			return;
		}

		if ( vOffset.x != 0 ) {
			pResult[ uCount++ ] = rect_t( vOffset.x > 0 ? sRect.x : r.w, sRect.y,
										  vOffset.x > 0 ? r.x : sRect.w, sRect.h );
		}

		if ( vOffset.y != 0 )
		{
			if ( vOffset.x != 0 )
			{
				pResult[ uCount++ ] = rect_t( vOffset.x > 0 ? r.x : sRect.x,
											  vOffset.y > 0 ? sRect.y : r.h,
											  vOffset.x > 0 ? sRect.w : r.w,
											  vOffset.y > 0 ? r.y : sRect.h );
			}
			else
			{
				pResult[ uCount++ ] = rect_t( sRect.x, vOffset.y > 0 ? sRect.y : r.h,
											  sRect.w, vOffset.y > 0 ? r.y : sRect.h );
			}
		}
	}
	
/*
=================================================
	булевская операция, результат - массив
	точек образовавшихся при смещении
	(смещение относительно левого нижнего угла)
=================================================
*/
	template <typename T, typename B>
	inline void BoolOpRectOffset(const TRect<T> &sRect, const TVec<T,2> &vOffset, TVec<B,2> *pResult, uint &uCount)
	{
		typedef TRect<T>	rect_t;

		const rect_t	r = sRect + vOffset;

		uCount = 0;

		if ( Any( Abs( vOffset ).LEqual( sRect.GetSize() ) ) )
		{
			_AddRectPoints( sRect, pResult, uCount );
			return;
		}

		if ( vOffset.x != 0 ) {
			_AddRectPoints( rect_t( vOffset.x > 0 ? sRect.x : r.w, sRect.y,
								    vOffset.x > 0 ? r.x : sRect.w, sRect.h ),
						    pResult, uCount );
		}

		if ( vOffset.y != 0 )
		{
			irect	tmp;

			if ( vOffset.x != 0 )
			{
				tmp = rect_t( vOffset.x > 0 ? r.x : sRect.x,
							  vOffset.y > 0 ? sRect.y : r.h,
							  vOffset.x > 0 ? sRect.w : r.w,
							  vOffset.y > 0 ? r.y : sRect.h );
			}
			else
			{
				tmp = rect_t( sRect.x, vOffset.y > 0 ? sRect.y : r.h,
							  sRect.w, vOffset.y > 0 ? r.y : sRect.h );
			}

			_AddRectPoints( tmp, pResult, uCount );
		}
	}
	
/*
=================================================
	выравнивание в большую сторону,
	должно быть степенью 2
=================================================
*/
	template <uint A, typename T>
	inline T Align(const T& value)
	{
		STATIC_ASSERT( (CompileTime::IsPowerOfTwo<uint, A>::value), "align must be power of two!" );

		const uint	ALIGN_POT	= CompileTime::PowerOfTwo<uint, A>::value;
		const uint	ALIGN_MASK	= (1 << ALIGN_POT) - 1;

		return ((value + ALIGN_MASK) & ~ALIGN_MASK);
	}

	template <uint A, typename T, usize I>
	inline TVec<T,I> Align(const TVec<T,I> &v)
	{
		TVec<T,I> res;
		FOR( i, res )	res[i] = Align<A>( v[i] );
		return res;
	}

	template <typename T>
	inline T Align(const T& value, const usize align)
	{
		//ASSERT( IsPowerOfTwo(align) );
		return ((value + (align-1)) / align) * align;
	}

	template <typename T, usize I>
	inline TVec<T,I> Align(const TVec<T,I>& value, const usize align)
	{
		TVec<T,I> res;
		FOR( i, res )	res[i] = Align( value[i], align );
		return res;
	}
	
/*
=================================================
	расчитывает коректировку нормализованных координат
	для соответствия пикселям
=================================================
*/
	template <typename T>
	inline TVec<T,2> ResCorrectionAspect (const TVec<T,2> &dim)
	{
		// proportional_coords = normalized_coords * aspect

		const TVec<T,2>	inv = T(1) / dim;
		return	inv.x > inv.y ?
				TVec<T,2>( T(1), inv.y / inv.x ) :
				TVec<T,2>( inv.x / inv.y, T(1) );
	}

//-------------------------------------------------------------------

}	// UXMath
}	// UX_STL

#endif	// _UXS_MATH_UTILS_H